package com.loren.oauth.web.controller;

import com.loren.oauth.data.dto.AccessTokenInfo;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.security.oauth2.OAuth2ClientProperties;
import org.springframework.http.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.jwt.Jwt;
import org.springframework.security.jwt.JwtHelper;
import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails;
import org.springframework.stereotype.Controller;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.HttpStatusCodeException;
import org.springframework.web.client.RestTemplate;

@Slf4j
@Controller
@RequestMapping
@AllArgsConstructor
public class OauthResourceController {

    private final OAuth2ClientProperties oauth2ClientProperties;

    private static final String OAUTH_CALLBACK_URL = "http://127.0.0.1:8081/oauth/callback";

    @GetMapping("/oauth/authorize")
    public String oauthAuthorize() {
        String clientId = this.oauth2ClientProperties.getClientId();
        return "redirect:http://127.0.0.1:9600/oauth/authorize?response_type=code&client_id=" + clientId
                + "&redirect_uri=" + OAUTH_CALLBACK_URL + "&state=abcd";
    }

    @ResponseBody
    @GetMapping("/oauth/callback")
    public Object oauthCallback(String code, String state) {
        log.info("code = {}, state = {}", code, state);
        String oauthAuthorizationUrl = "http://127.0.0.1:9600/oauth/token";
        HttpHeaders headers = new HttpHeaders();
        headers.setBasicAuth(this.oauth2ClientProperties.getClientId(), this.oauth2ClientProperties.getClientSecret());
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        params.add("code", code);
        params.add("redirect_uri", OAUTH_CALLBACK_URL);
        params.add("grant_type", "authorization_code");
        HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(params, headers);
        RestTemplate restTemplate = new RestTemplate();
        try {
            ResponseEntity<AccessTokenInfo> accessTokenResponse = restTemplate.exchange(oauthAuthorizationUrl, HttpMethod.POST, entity, AccessTokenInfo.class);
            return accessTokenResponse.getBody();
        } catch (HttpStatusCodeException e) {
            return e.getResponseBodyAsString();
        }
    }

    @ResponseBody
    @GetMapping("/user-info")
    public Object userInfo() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) authentication.getDetails();
        log.info("access_token = {}", details.getTokenValue());
        Jwt decode = JwtHelper.decode(details.getTokenValue());
        return decode.getClaims();
    }
}
